#pragma once
#include "graph.hpp"
#include "map.hpp"
#include <sstream>
#include <cmath>
#include <boost/optional.hpp>

extern unsigned int MaxPeople;

enum {
	// ReportTime is the time to sense a room and report
	// injured people in the room.
	ReportTime = 1,

	// RescueReward is the amount of reward acquired
	// for rescuing people.  This is treated as the cost of
	// not rescuing people before ending.
	RescueReward = 1,
};

// A Op is an operator that can be performed
// by the agent in the rescue domain.
class Op  {
public:
	enum { None, Report, Move, Skip };

	// This constructs a no-op.
	Op() : kind(None) { }

	Op(char k) : kind(k) { }

	Op(char k, int v) : kind(k), value(v) { }

	Op(const Op &o) : kind(o.kind), value(o.value) { }

	bool operator==(const Op &o) const {
		if (kind != o.kind)
			return false;
		if (kind == Skip || kind == Move)
			return value == o.value;
		return true;
	}

	bool operator!=(const Op &o) const {
		return !(*this == o);
	}

	std::string str() const {
		std::stringstream s;
		switch (kind) {
		case None:
			s << "Nop";
			break;

		case Report:
			s << "Report";
			break;

		case Move:
			s << "Move " << value;
			break;

		case Skip:
			s << "Skip " << value;
			break;

		default:
			s << "Unknown (" << (int) kind << ")";
		}
		return s.str();
	}

	int kind;

	// For a Skip this is the index of the people to skip,
	// For a Move this is the room to which the agent
	// should move.  It is invalid for anything else.
	int value;
};

// CostTime is a lexically ordered type on first
// cost then time.
class CostTime {
public:
	CostTime() : cost(0), time(0) { }

	CostTime(double c, double t) : cost(c), time(t) { }

	static CostTime inf() {
		return CostTime(std::numeric_limits<double>::infinity(),
			std::numeric_limits<double>::infinity());
	}

	CostTime &operator+=(const CostTime &o) {
		cost += o.cost;
		time += o.time;
		return *this;
	}

	CostTime &operator/=(const CostTime &o) {
		cost /= o.cost;
		time /= o.time;
		return *this;
	}

	bool operator==(const CostTime &o) const {
		return cost == o.cost && time == o.time;
	}

	bool operator<(const CostTime &o) const {
		if (cost == o.cost)
			return time < o.time;
		return cost < o.cost;
	}

	double cost;
	double time;
};


class Rescue {
public:

	Rescue() { }

	// Rescue constructs a new world with the
	// given map, set of injured people and
	// goal (end node and deadline).  The
	// initial location is 0.
	Rescue(const Map&, const std::vector<int>&, int, double);

	Rescue(const Map&, const std::vector<int>&, int home, int cur, double);

	// Consistent returns true if futures generated in
	// the target of the method will be consistent with
	// the argument.
	bool consistent(const Rescue&) const;

	// Ops returns all applicable operators.
	std::vector<boost::optional<Op> > ops() const;

	// Act performs the given operation.
	void act(boost::optional<Op>);

	// Sense reconsiles a partial world with
	// the true world.
	void sense(const Rescue&);

	// Validateop checks that the operator is
	// valid in the given world.
	void validateop(boost::optional<Op> op, std::vector<Op> ops) const;

	// Draw draws the world.
	void draw(std::string, const std::vector<Op>&) const;

	// Dump dumps the world to the given file.
	void dump(FILE*) const;

	unsigned int npeople() const;

	// Solve runs a deterministic solver on the world,
	// returning the optimal solution path.
	struct Solution {
		std::vector<Op> ops;
		CostTime ct;
	};

	Solution solve() const;

	// Future returns a possible future.
	Rescue future(unsigned int) const;

	// Knitallyknown returns a world that is the same
	// as the target, however with everything that
	// is initially unknown removed.
	Rescue initallyknown() const;

	// Done is called when the simulator
	// is finished.  It should print various
	// information about the execution.
	void done(FILE*, const std::vector<Op>&) const;


	// Southonly and westonly bias the placement
	// of people in the map to be only south of
	// the home point and only west of the home
	// point respectively.
	bool southonly, westonly;


	// M is the world map.
	Map m;

	// Visited is true if the agent has visited
	// the corresponding node.
	std::vector<bool> visited;

	// People contains the node indices of all
	// injured people.  -1 indicates a place-holder
	// and should be skipped.  This is used to
	// simplify the implementation of unseen
	// people in the simulator.
	std::vector<int> people;

	// Reported is true if the corresponding
	// person has been reported.
	std::vector<bool> reported;

	// Loc is the current node id at which
	// the robot is located.
	int loc;

	// Time is the current time.
	double time;

	// Home is the node ID of the start and end node
	// at which the robot must be by the time the
	// deadline arrives.
	int home;

	// Deadline is the deadline at which
	// time the robot must be at the end
	// node.
	double deadline;
};
